//==============================================================================
// Copyright (c) 2008-2013 Niall Ryan. All Rights Reserved.
//==============================================================================

#ifndef HYDRA_INTERNAL_THREADLOCAL_INTERNAL_H
#define HYDRA_INTERNAL_THREADLOCAL_INTERNAL_H 1

#include "hydra/locks/TTASLock.h"
#include "hydra/typetraits.h"

namespace Hydra
{
	namespace Internal
	{
		template<size_t ElementSize>
		class ThreadLocalStorage
		{
		public:
			ThreadLocalStorage()
			{
				assert(STORAGE_SIZE<=0xffff);	//since we use shorts in the free list

				for( int i=0; i<STORAGE_SIZE; ++i )
					m_stack[i] = STORAGE_SIZE-i-1;	//reverse order for convenience, so our indices will start at 0
				m_stackSize = STORAGE_SIZE;
			}

			static ThreadLocalStorage& Instance()		{ return s_instance; }

			unsigned int allocIndex()
			{
				//pop the next free index off the stack
				m_lock.lock();
				assert(m_stackSize>0);
				--m_stackSize;
				unsigned short index = m_stack[m_stackSize];
				m_lock.unlock();

				return index;
			}

			void freeIndex(unsigned int index)
			{
				//push the index back onto the stack
				m_lock.lock();
				assert(m_stackSize<STORAGE_SIZE);
				m_stack[m_stackSize] = index;
				++m_stackSize;
				memset(get(index), 0, ElementSize);	//set back to zero, all tls memory is initialized to 0
				m_lock.unlock();
			}

			void* get(unsigned int index)				{ return &s_store[index*ElementSize]; }

		private:
			enum
			{
				STORAGE_SIZE = 1024
			};

			static ThreadLocalStorage s_instance;

			__declspec(thread) static char s_store[STORAGE_SIZE*ElementSize];

			TTASLock m_lock;
			unsigned short m_stack[STORAGE_SIZE];
			int m_stackSize;
		};

		template<size_t ElementSize> ThreadLocalStorage<ElementSize> ThreadLocalStorage<ElementSize>::s_instance;
		template<size_t ElementSize> __declspec(thread) char ThreadLocalStorage<ElementSize>::s_store[STORAGE_SIZE*ElementSize];

		//handles the destruction of ThreadLocal objects, keeps a list of all the constructed objects and deletes them when the ThreadLocal is destroyed
		template<typename T, bool isHeapAlloc>
		class ThreadLocalDestructionList
		{
		public:
			~ThreadLocalDestructionList()
			{
				for( unsigned int i=0; i<m_list.size(); ++i )
				{
					if( isHeapAlloc )
						delete m_list[i];
					else
						m_list[i]->~T();
				}
			}
			void registerForDestruction(T* ptr)
			{
				//need to lock here, can be called by multiple threads
				m_lock.lock();
				m_list.push_back(ptr);
				m_lock.unlock();
			}
		private:
			TTASLock m_lock;
			std::vector<T*> m_list;
		};

		//heap allocator for thread local objects, just the pointer is stored in TLS, each thread constructs its instance on first use
		template<typename T>
		class ThreadLocalHeapAlloc
		{
			typedef ThreadLocalStorage<4> Storage;
		public:
			unsigned int allocIndex()			{ return Storage::Instance().allocIndex(); }
			void freeIndex(unsigned int index)	{ Storage::Instance().freeIndex(index); }
			T* get(unsigned int index)
			{
				T** storage = static_cast<T**>(Storage::Instance().get(index));
				if( !(*storage) )
				{
					*storage = new T;
					m_destructionList.registerForDestruction(*storage);
				}
				return *storage;
			}
		private:
			ThreadLocalDestructionList<T, true> m_destructionList;
		};

		//allocator for thread local objects which are stored entirely in TLS. Extra storage is used to indicate if each threads instance has been constructed
		template<typename T, size_t Size, bool isTrivialConstructor, bool isTrivialDestructor>
		class ThreadLocalAlloc
		{
			typedef ThreadLocalStorage<Size+4> Storage;
		public:
			unsigned int allocIndex()			{ return Storage::Instance().allocIndex(); }
			void freeIndex(unsigned int index)	{ Storage::Instance().freeIndex(index); }
			T* get(unsigned int index)
			{
				int* ptr = static_cast<int*>(Storage::Instance().get(index));
				int isConstructed = *ptr;
				T* objPtr = reinterpret_cast<T*>(ptr+1);
				if( !isConstructed )
				{
					*ptr = 1;
					new(objPtr) T;
					m_destructionList.registerForDestruction(objPtr);
				}
				return objPtr;
			}
		private:
			ThreadLocalDestructionList<T, false> m_destructionList;
		};

		//specialization for thread local objects with a constructor but no destructor, they must be constructed on first use, but do not need to be deleted
		template<typename T, size_t Size>
		class ThreadLocalAlloc<T, Size, false, true>
		{
			typedef ThreadLocalStorage<Size+4> Storage;
		public:
			unsigned int allocIndex()			{ return Storage::Instance().allocIndex(); }
			void freeIndex(unsigned int index)	{ Storage::Instance().freeIndex(index); }
			T* get(unsigned int index)
			{
				int* ptr = static_cast<int*>(Storage::Instance().get(index));
				int isConstructed = *ptr;
				T* objPtr = reinterpret_cast<T*>(ptr+1);
				if( !isConstructed )
				{
					*ptr = 1;
					new(objPtr) T;
				}
				return objPtr;
			}
		};

		//specialization for thread local objects without constructors or destructors which are stored entirely in TLS. No construct on first use code is necessary
		template<typename T, size_t Size>
		class ThreadLocalAlloc<T, Size, true, true>
		{
		public:
			typedef ThreadLocalStorage<Size> Storage;
			unsigned int allocIndex()			{ return Storage::Instance().allocIndex(); }
			void freeIndex(unsigned int index)	{ Storage::Instance().freeIndex(index); }
			T* get(unsigned int index)			{ return static_cast<T*>(Storage::Instance().get(index)); }
		};

		template<typename T, size_t Size>
		struct ThreadLocalPolicies
		{
			typedef ThreadLocalHeapAlloc<T> Alloc;
		};

		template<typename T>
		struct ThreadLocalPolicies<T, 4>
		{
			typedef ThreadLocalAlloc<T, 4, TypeTraits::has_trivial_constructor<T>::value, TypeTraits::has_trivial_destructor<T>::value> Alloc;
		};

		template<typename T>
		struct ThreadLocalPolicies<T, 3>
		{
			typedef ThreadLocalAlloc<T, 4, TypeTraits::has_trivial_constructor<T>::value, TypeTraits::has_trivial_destructor<T>::value> Alloc;
		};

		template<typename T>
		struct ThreadLocalPolicies<T, 2>
		{
			typedef ThreadLocalAlloc<T, 4, TypeTraits::has_trivial_constructor<T>::value, TypeTraits::has_trivial_destructor<T>::value> Alloc;
		};

		template<typename T>
		struct ThreadLocalPolicies<T, 1>
		{
			typedef ThreadLocalAlloc<T, 4, TypeTraits::has_trivial_constructor<T>::value, TypeTraits::has_trivial_destructor<T>::value> Alloc;
		};
	}
}

#endif